﻿// -----------------------------------------------------------------------
// <copyright file="Utilities.cs" company="Microsoft Corporation">
//     Copyright (c) Microsoft Corporation 2011. All rights reserved.
// </copyright>
// -----------------------------------------------------------------------

namespace Internal.TeamFoundationServer.Submit
{
    using System;
    using System.Diagnostics;
    using System.DirectoryServices.AccountManagement;
    using System.Globalization;
    using System.Linq;
    using System.Net;
    using System.Net.Mail;
    using System.Text;
    using System.Windows.Forms;
    using Properties;

    /// <summary>
    /// Utilities class.
    /// </summary>
    internal static class Utilities
    {
        /// <summary>
        /// Constant string for name separator.
        /// </summary>
        internal const char NameSeparator = ';';

        /// <summary>
        /// AssertArgumentNull. Used to Debug.Assert null arguments.
        /// Should be used in all public method to make sure methods are not receiving null arguments.
        /// </summary>
        /// <param name="obj">Object to validate.</param>
        /// <param name="argumentName">Argument name.</param>
        internal static void AssertArgumentNull(object obj, string argumentName)
        {
            Debug.Assert(obj != null, string.Format(CultureInfo.InvariantCulture, "Argument {0} should never be null", argumentName));
        }

        /// <summary>
        /// ValidateUser event.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="e">Event arguments object.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "We will fix it in the next version of TFS Submit.")]
        internal static void ValidateUser(object sender, KeyEventArgs e)
        {
            AssertArgumentNull(sender, "sender");
            AssertArgumentNull(e, "e");

            if (sender == null)
            {
                return;
            }

            var textBox = sender as TextBox;
            if (textBox == null || e.KeyData != (Keys.Control | Keys.K))
            {
                return;
            }

            try
            {
                var aliases = textBox.Text;
                var stringBuilder = new StringBuilder();
                foreach (var domain in TfsSubmitConfig.DomainFullyQualifiedName.Split(new[] { ',', ';' }).Select(str => str.Trim()).Where(domain => domain.Length != 0))
                {
                    // Get principal context.
                    using (var principalContext = new PrincipalContext(ContextType.Domain, domain))
                    {
                        // Now try to resolve emails.
                        foreach (var token in aliases.Split(new[] { ',', ';' }).Select(str => str.Trim()).Where(token => token.Length != 0))
                        {
                            try
                            {
                                // if was able to find the user, get info.
                                using (var userPrincipal = UserPrincipal.FindByIdentity(principalContext, token))
                                {
                                    if (userPrincipal != null)
                                    {
                                        if (!stringBuilder.ToString().Contains(userPrincipal.SamAccountName))
                                        {
                                            if (stringBuilder.Length > 0)
                                            {
                                                stringBuilder.Append(NameSeparator + " ");
                                            }

                                            stringBuilder.Append(userPrincipal.SamAccountName);
                                        }
                                    }
                                    else
                                    {
                                        // otherwise, maybe this is a Distribution list.
                                        using (
                                            var groupPrincipal = GroupPrincipal.FindByIdentity(
                                                principalContext, token))
                                        {
                                            // If groupPrincipal is null, token is invalid.
                                            if (groupPrincipal != null)
                                            {
                                                if (
                                                    !stringBuilder.ToString().Contains(
                                                        groupPrincipal.SamAccountName))
                                                {
                                                    if (stringBuilder.Length > 0)
                                                    {
                                                        stringBuilder.Append(NameSeparator + " ");
                                                    }

                                                    stringBuilder.Append(groupPrincipal.SamAccountName);
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                            catch (MultipleMatchesException)
                            {
                                // At this point there is nothing we can do.
                                // Ignore the exception and continue.
                            }
                        }
                    }
                }

                textBox.Text = stringBuilder.ToString();
            }
            catch (PrincipalServerDownException)
            {
                // There is nothing we can do here.
                // This should never happen.
                // If Redmond domain is down the entire network is down.
            }
        }

        /// <summary>
        /// Lookup all DLs and return the members.
        /// </summary>
        /// <param name="aliases">List with aliases.</param>
        /// <returns>String with all aliases.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity", Justification = "We will fix it in the next version of TFS Submit.")]
        internal static string ExpandDistributionLists(string aliases)
        {
            AssertArgumentNull(aliases, "aliases");

            // Now try to resolve alias.
            var stringBuilder = new StringBuilder();
            foreach (var domain in TfsSubmitConfig.DomainFullyQualifiedName.Split(new[] { ',', ';' }).Select(str => str.Trim()).Where(domain => domain.Length != 0))
            {
                // Get principal context.
                using (var principalContext = new PrincipalContext(ContextType.Domain, domain))
                {
                    foreach (var token in aliases.Split(new[] { ',', ';' }).Select(str => str.Trim()).Where(token => token.Length != 0))
                    {
                        try
                        {
                            // check if alias is a Distribution group.
                            using (var groupPrincipal = GroupPrincipal.FindByIdentity(principalContext, token))
                            {
                                // If groupPrincipal is null, token is user already.
                                if (groupPrincipal != null)
                                {
                                    using (var principalSearchResult = groupPrincipal.GetMembers(true))
                                    {
                                        foreach (var principal in principalSearchResult.Where(principal => !stringBuilder.ToString().Contains(principal.SamAccountName)))
                                        {
                                            if (stringBuilder.Length > 0)
                                            {
                                                stringBuilder.Append(NameSeparator + " ");
                                            }

                                            stringBuilder.Append(principal.SamAccountName);
                                        }
                                    }
                                }
                                else
                                {
                                    // if it is not a DL, check if it is a valid user.
                                    using (var userPrincipal = UserPrincipal.FindByIdentity(principalContext, token))
                                    {
                                        if (userPrincipal != null)
                                        {
                                            if (!stringBuilder.ToString().Contains(userPrincipal.SamAccountName))
                                            {
                                                if (stringBuilder.Length > 0)
                                                {
                                                    stringBuilder.Append(NameSeparator + " ");
                                                }

                                                stringBuilder.Append(userPrincipal.SamAccountName);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                        catch (MultipleMatchesException)
                        {
                            // At this point there is nothing we can do.
                            // Ignore the exception and continue.
                        }
                    }
                }
            }

            return stringBuilder.ToString();
        }

        /// <summary>
        /// Fix the width of the drop down box.
        /// </summary>
        /// <param name="sender">Object sender.</param>
        /// <param name="toolStripComboBox">Combo object to be fixed.</param>
        internal static void FixComboItemsWidth(object sender, ToolStripComboBox toolStripComboBox)
        {
            AssertArgumentNull(sender, "sender");
            AssertArgumentNull(toolStripComboBox, "toolStripComboBox");

            var width = toolStripComboBox.Width;
            var g = ((Control)sender).CreateGraphics();
            var font = toolStripComboBox.Font;

            // checks if a scrollbar will be displayed.
            // If yes, then get its width to adjust the size of the drop down list.
            var vertScrollBarWidth = (toolStripComboBox.Items.Count > toolStripComboBox.MaxDropDownItems) ? SystemInformation.VerticalScrollBarWidth : 0;

            // Loop through list items and check size of each items.
            // set the width of the drop down list to the width of the largest item.
            width = (from string item in toolStripComboBox.Items where item != null select (int)g.MeasureString(item.Trim(), font).Width + vertScrollBarWidth).Concat(new[] { width }).Max();
            toolStripComboBox.DropDownWidth = width;
        }
        
        /// <summary>
        /// Send an email with the exception information for developer.
        /// </summary>
        /// <param name="exception">Exception object.</param>
        internal static void ReportException(Exception exception)
        {
            try
            {
                using (var mailMessage = new MailMessage())
                {
                    mailMessage.Subject = Resources.ReportExceptionTitle;
                    mailMessage.Body = exception + Environment.NewLine;
                    mailMessage.IsBodyHtml = true;
                    mailMessage.To.Add(new MailAddress("tfssubmit@microsoft.com"));
                    mailMessage.To.Add(new MailAddress(Environment.UserName + "@" + TfsSubmitConfig.MailDomainName));

                    foreach (var domain in TfsSubmitConfig.DomainFullyQualifiedName.Split(new[] { ',', ';' }).Select(str => str.Trim()).Where(domain => domain.Length != 0))
                    {
                        using (var principalContext = new PrincipalContext(ContextType.Domain, domain))
                        {
                            using (var userPrincipal = UserPrincipal.FindByIdentity(principalContext, Environment.UserName))
                            {
                                mailMessage.From = userPrincipal != null ? new MailAddress(userPrincipal.EmailAddress) : mailMessage.To[0];
                            }

                            using (var smtp = new SmtpClient())
                            {
                                smtp.Host = TfsSubmitConfig.SmtpServer;
                                smtp.Credentials = CredentialCache.DefaultNetworkCredentials;

                                // Send Email.
                                smtp.Send(mailMessage);
                                break;
                            }
                        }
                    }
                }
            }
            catch (SmtpException err)
            {
                MessageBox.Show(
                    err.Message,
                    Resources.ErrorString,
                    MessageBoxButtons.OK,
                    MessageBoxIcon.Error,
                    MessageBoxDefaultButton.Button1,
                    MessageBoxOptions.RightAlign & MessageBoxOptions.RtlReading);
            }
        }

        /// <summary>
        /// Used to get Default values previous used by user..
        /// </summary>
        /// <param name="toolStripComboBox">Dropdown object.</param>
        /// <param name="value">String value.</param>
        internal static void GetDefaultValue(ToolStripComboBox toolStripComboBox, string value)
        {
            if (!string.IsNullOrEmpty(value))
            {
                for (var i = 0; i < toolStripComboBox.Items.Count; i++)
                {
                    if ((string)toolStripComboBox.Items[i] == value)
                    {
                        toolStripComboBox.SelectedIndex = i;
                        return;
                    }
                }
            }

            toolStripComboBox.SelectedIndex = 0;
        }

        /// <summary>
        /// Used to get Default values previous used by user..
        /// </summary>
        /// <param name="propertyName">Property Name.</param>
        /// <param name="value">String value.</param>
        internal static void SetDefaultValue(string propertyName, string value)
        {
            if (!string.IsNullOrEmpty(value))
            {
                var settingsProperty = Settings.Default.Properties[propertyName];
                if (settingsProperty != null)
                {
                    settingsProperty.DefaultValue = value;
                    Settings.Default.Save();
                    Settings.Default.Reload();
                }
            }
        }
    }
}
